<?php

namespace Services\Request;

use GuzzleHttp\Exception\RequestException;
use Services\Log\LogService;
use Services\WxGongZhong\Exceptions\WxGongZhongException;

class WxGongZhongRequest extends BaseRequest
{
    /**
     * 应用id
     * @var mixed|string
     */
    private $appid = '';

    /**
     * 应用key
     * @var mixed|string
     */
    private $secret = '';

    /**
     * 自定义业务侧响应成功状态码
     * 默认0表示成功,非0则失败
     * 可根据对接的三方情况相互约定成功响应的code值
     * @var int
     */
    public $successCode = 0;

    /**
     * 请求超时时间,单位:s
     * @var int
     */
    public $timeout = 10;

    /**
     * 是否开启调试 true:开启 false:关闭
     * @var bool
     */
    public $debug = false;

    /**
     * 是否自动加签, true:是 false:否
     * 开启的话,接口请求默认会追加sign参数进行透传
     * @var bool
     */
    public $sign = false;


    //请求接口path
    public const API = [
        'token' => 'cgi-bin/token',
        'qrcode_create' => 'cgi-bin/qrcode/create',
        'show_qrcode' => 'cgi-bin/showqrcode',//换取二维码需要切换域名
        'user_info' => 'cgi-bin/user/info',//通过openid拉取用户信息
        'template_send' => 'cgi-bin/message/template/send',//发送模版消息
        'create_menu' => 'cgi-bin/menu/create',//创建菜单
    ];

    public function __construct($config = [])
    {
        $this->appid = arr_get($config, 'appid', '');
        $this->secret = arr_get($config, 'secret', '');
        $this->initBaseUri();
    }

    /**
     * 定义请求三方接口的基础url
     * @return string
     */
    public function setBaseUri(): string
    {
        return 'https://api.weixin.qq.com/';
    }

    public function initBaseUri()
    {
        parent::initBaseUri();
    }

    public function getAccessToken($reset = false)
    {
        $key = 'wx_gongzhong_v3_' . $this->appid;
        if ($reset) {
            S($key, null);
        }
        $token = S($key);
        if ($token) {
            return $token;
        }
        $result = $this->buildRequest(self::API['token'], [
            'grant_type' => 'client_credential',
            'appid' => $this->appid,
            'secret' => $this->secret
        ], 'get');
        if ($result) {
            $result = arr_get($result, 'access_token');
            LogService::write($result, 'wx_gongzhong_access_token');
            S($key, $result, 7000);
        }
        return $result;
    }

    public function qrcodeCreate($options = [])
    {
        $this->getAccessToken();
        $params = [
            'action_name' => 'QR_LIMIT_STR_SCENE',
            'action_info' => [
                'scene' => [
                    'scene_str' => arr_get($options, 'scene_str', 'user_bind')
                ]
            ]
        ];
        $uri = self::API['qrcode_create'] . '?access_token=' . $this->getAccessToken();
        $qrcodeResult = $this->buildRequest($uri, $params, 'json');
        $ticket = arr_get($qrcodeResult, 'ticket', '');
        if (!$ticket) {
            throw new RequestException('qrcode create fail');
        }
        //https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET 提醒：TICKET记得进行UrlEncode
        $tempLink = 'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=' . urlencode($ticket);
        return $tempLink;
    }

    public function showQrcode($ticket)
    {
        $ticket = urlencode($ticket);
        bin_dump($ticket);
        $uri = self::API['show_qrcode'];
        //重置域名
        $this->baseUri = 'https://mp.weixin.qq.com/';
        return $this->buildRequest($uri, ['ticket' => $ticket], 'get');
    }

    public function userInfo($openId, $time = 1, $resetToken = false)
    {
        if ($time >= 5) {
            throw new WxGongZhongException('模板消息推送重试超时');
        }
        $params = [
            'access_token' => $this->getAccessToken(),
            'openid' => $openId
        ];
        $uri = self::API['user_info'] . '?' . http_build_query([
                'access_token' => $this->getAccessToken($resetToken),
                'openid' => $openId,
                'lang' => 'zh_CN'
            ]);
        $result = $this->buildRequest($uri, $params, 'get');
        $errcode = arr_get($result, 'errcode', 0);
        if ($errcode == 40001) {
            sleep(1);
            return $this->userInfo($openId, $time + 1, true);
        }
        return $result;
    }

    public function sendTemplateMessage($options = [], $time = 1, $resetToken = false)
    {
        if ($time >= 5) {
            throw new WxGongZhongException('模板消息推送重试超时');
        }
        $params = [
            'touser' => arr_get($options, 'touser'),
            'template_id' => arr_get($options, 'template_id'),
            'data' => arr_get($options, 'data'),
            'appid' => arr_get($options, 'appid'),
        ];
        if ($miniprogram = arr_get($options, 'miniprogram')) {
            $params['miniprogram'] = $miniprogram;
        }
        if ($url = arr_get($options, 'url')) {
            $params['url'] = $url;
        }
        $uri = self::API['template_send'] . '?' . http_build_query([
                'access_token' => $this->getAccessToken($resetToken),
            ]);
        $result = $this->buildRequest($uri, $params, 'json');
        $errcode = arr_get($result, 'errcode', 0);
        if ($errcode == 40001) {
            sleep(1);
            return $this->sendTemplateMessage($options, $time + 1, true);
        }
        return $result;
    }

    public function createMenu($options = [])
    {
        $params = [
            'button' => arr_get($options, 'button')
        ];
        $uri = self::API['create_menu'] . '?' . http_build_query([
                'access_token' => $this->getAccessToken(),
            ]);
        $this->setRequestOptions([
            'headers' => [
                'Content-Type' => 'application/json; charset=utf-8'
            ],
            'body' => json_encode($params, JSON_UNESCAPED_UNICODE)
        ]);
        return $this->buildRequest($uri, $params, 'postv2');
    }

    /**
     * 重写父类的响应处理,针对不同的三方,则自定义最终响应结果该如何处理
     * @return void
     * @throws \Exception
     */
    public function parseResponse()
    {
        try {
            $result = $this->getContent();
            if (empty($result)) {
                throw new \Exception('公众号请求失败');
            }
//            $uri = $this->getUri();
//            if ($uri == self::API['token'] && arr_get($result, 'access_token')) {
//                $this->setContent(arr_get($result, 'access_token'));
//            }
        } catch (\Throwable $exception) {
            LogService::writeWithDb([
                'base_uri' => $this->getBaseUri(),
                'url' => $this->getUri(),
                'params' => $this->getParams(),
                'method' => $this->getMethod(),
                'body' => $this->getBody(),
                'content' => $this->getContent(),
                'message' => $exception->getMessage(),
            ], 'wx_gongzhong_request_error', '公众号请求类');
            throw $exception;
        }
    }
}
